#include <pthread.h>
#include <queue>

namespace MyBqueue
{
    const int gcapacity = 5;
    template <class T>
    class BlockQueue
    {

        bool IsFull()
        {
            return _capacity == _que.size();
        }

        bool IsEmpty()
        {
            return _que.size() == 0;
        }

    public:
        BlockQueue(int capacity = gcapacity) : _capacity(capacity)
        {
            pthread_mutex_init(&_mutex, nullptr);
            pthread_cond_init(&_consumer_cond, nullptr);
            pthread_cond_init(&_product_cond, nullptr);
        }

        void push(const T &data)
        {
            pthread_mutex_lock(&_mutex);

            while (IsFull())
            {
                pthread_cond_wait(&_product_cond, &_mutex);
            }
            _que.push(data);

            pthread_mutex_unlock(&_mutex);
            pthread_cond_signal(&_consumer_cond);
        }

        const T &pop()
        {
            pthread_mutex_lock(&_mutex);


            while (IsEmpty())
            {
                pthread_cond_wait(&_consumer_cond, &_mutex);
            }


            const T &ret = _que.front();
            _que.pop();

            pthread_mutex_unlock(&_mutex);
            pthread_cond_signal(&_product_cond);

            return ret;
        }

        ~BlockQueue()
        {
            pthread_mutex_destroy(&_mutex);

            pthread_cond_destroy(&_product_cond);
            pthread_cond_destroy(&_consumer_cond);
        }

    private:
        std::queue<T> _que;
        int _capacity;

        pthread_mutex_t _mutex;
        pthread_cond_t _consumer_cond;
        pthread_cond_t _product_cond;
    };
}
